/*
 * Copyright 2011-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0.
 * See `LICENSE` in the project root for license information.
 */

package me.ijleex.platform.framework.util;

import java.nio.ByteBuffer;
import java.security.SecureRandom;
import java.util.UUID;

/**
 * 生成UUIDv7.
 *
 * <p>UUIDv7 是基于时间戳的有序 UUID 版本，具有以下特点：</p>
 *
 * <pre>
 * ①时间戳前缀：前 48 位为毫秒级时间戳，保证有序性。
 * ②分布式友好：适合分布式系统生成唯一ID。
 * ③可读性高：时间戳部分便于调试和排序。</p>
 * </pre>
 *
 * <p><b>UUIDv7结构：</b></p>
 *
 * <pre> {@code
 * 01948418-acbe-7a6d-ae56-140f8be21dde
 * └─timestamp─┘ │└─┤ │└───rand_b─────┘
 *              ver │var
 *               rand_a
 * }
 * </pre>
 *
 * @author liym
 * @see <a href="https://antonz.org/uuidv7/">UUIDv7 in 33 languages</>
 * @since 2025-01-20 22:06 新建
 */
public final class UUIDv7 {

    private static final SecureRandom RANDOM = new SecureRandom();

    private UUIDv7() {
        throw new IllegalAccessError("Utility class");
    }

    /**
     * 生成 UUIDv7
     *
     * @return UUIDv7
     * @see UUID#randomUUID()
     */
    public static UUID randomUUID() {
        byte[] value = randomBytes();
        ByteBuffer buf = ByteBuffer.wrap(value);
        long high = buf.getLong();
        long low = buf.getLong();
        return new UUID(high, low);
    }

    private static byte[] randomBytes() {
        // current timestamp in ms
        ByteBuffer timestamp = ByteBuffer.allocate(Long.BYTES);
        timestamp.putLong(System.currentTimeMillis());

        // random bytes
        final byte[] value = new byte[16];
        RANDOM.nextBytes(value);

        // Set first 48 bits to unix epoch timestamp
        System.arraycopy(timestamp.array(), 2, value, 0, 6);

        /*
         * Set magic numbers for a "version 7" UUID, see
         * https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-00.html#name-uuid-version-7
         */
        value[6] = (byte) ((value[6] & 0x0F) | 0x70); // 4 bit version [0111]
        value[8] = (byte) ((value[8] & 0x3F) | 0x80); // 2 bit variant [10]

        return value;
    }

    /**
     * 从 UUIDv7 中提取生成UUID使用的时间戳
     *
     * @param uuid UUIDv7
     * @return 时间戳（毫秒）
     * @see UUID#version()
     * @see UUID#timestamp()
     * @since 2025-03-16 21:08
     */
    public static long timestamp(String uuid) {
        return timestamp(UUID.fromString(uuid));
    }

    /**
     * 从 UUIDv7 中提取生成UUID使用的时间戳
     *
     * @param uuid UUIDv7
     * @return 时间戳（毫秒）
     * @see UUID#version()
     * @see UUID#timestamp()
     * @since 2025-03-16 21:06
     */
    public static long timestamp(UUID uuid) {
        if (uuid.version() != 7) {
            throw new IllegalArgumentException("Not a v7 UUID");
        }
        long high = uuid.getMostSignificantBits();
        return getEpochTimestamp(high);
    }

    private static long getEpochTimestamp(long msb) {
        return (msb & 0xFFFFFFFFFFFF0000L) >>> 16;
    }

}
